﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using TransportAbstractionLibrary.Common.BufferPool;
using TransportAbstractionLibrary.Common;

namespace TransportAbstractionLibrary.UDP
{
    public abstract partial class UDPServer : IAsyncHost
    {
        private class SendItem
        {
            public readonly UDPConnection Connection;
            public readonly byte[] Data;

            public SendItem(UDPConnection connection, byte[] data)
            {
                this.Connection = connection;
                this.Data = data;
            }
        }

        List<UDPConnection> connectionList = new List<UDPConnection>();
        object connectionListLocker = new object();

        BufferManager bufferManager;
        int port;
        object locker = new object();
        bool running;
        Socket socket;
        bool zeroIsDisconnect;

        public void ConnectTo(IPAddress ipAddress, int port)
        {
            UDPConnection connection = new UDPConnection(this, new IPEndPoint(ipAddress, port));
            lock (connectionList)
            {
                connectionList.Add(connection);
            }
            InboundConnection(connection);
            connection.Initialize();
        }

        public int Port
        {
            get { return port; }
        }

        internal void Disconnect(UDPConnection connection)
        {
            lock (connectionListLocker)
            {
                for (int i = 0; i < connectionList.Count; i++)
                {
                    if (connectionList[i] == connection)
                    {
                        connectionList[i].ConnectionClosed();
                        connectionList.RemoveAt(i);
                        return;
                    }
                }
            }
            throw new TALSocketException(connection, "Can't disconnect a connection if its not in the connectionList");
        }

        public void Start()
        {
            lock (locker)
            {
                if (!running)
                {
                    running = true;
                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    socket.Bind(new IPEndPoint(IPAddress.Any, port));
                    Receive();
                }
                else
                {
                    throw new ArgumentException("Is already listening!");
                }
            }

        }

        public void Stop()
        {
            lock (locker)
            {
                if (running)
                {
                    socket.Close();
                    running = false;
                }
                else
                {
                    throw new ArgumentException("Is not listening!");
                }
            }
        }

        public UDPServer(int port, BufferManager bufferManager, bool zeroIsDisconnect)
        {
            this.port = port;
            this.bufferManager = bufferManager;
            this.zeroIsDisconnect = zeroIsDisconnect;
            InitializeRead();
            InitializeSend();
        }

        #region IAsyncHost Members

        IProtocolBase IAsyncHost.GetProtocolBase(SocketEventArgs args)
        {
            lock (connectionListLocker)
            {
                for (int i = 0; i < connectionList.Count; i++)
                {
                    if (connectionList[i].EndPoint == args.RemoteEndPoint)
                    {
                        return connectionList[i];
                    }
                }
            }
            throw new ArgumentException("Could not find connection!");
        }

        #endregion
    }
}
